// Package service internal/service/auth_service.go
package service

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/mojocn/base64Captcha"
	"gozrd-admin-api/internal/entity/dto"
	"gozrd-admin-api/internal/entity/model"
	"gozrd-admin-api/internal/entity/vo"
	"gozrd-admin-api/internal/repository"
	"gozrd-admin-api/pkg/auth"
	"gozrd-admin-api/pkg/response"
	"gozrd-admin-api/pkg/util"
)

// ILoginService 认证 服务接口
type ILoginService interface {
	Login(c *gin.Context, dto dto.LoginDto)
}

type LoginService struct {
	adminRepository     repository.IAdminRepository
	menuRepository      repository.IMenuRepository
	loginInfoRepository repository.ILoginInfoRepository
	validator           *validator.Validate
	captchaStore        base64Captcha.Store
}

func NewLoginService(adminRepo repository.IAdminRepository,
	menuRepository repository.IMenuRepository,
	loginInfoRepo repository.ILoginInfoRepository,
	captchaStore base64Captcha.Store) ILoginService {
	return &LoginService{
		adminRepository:     adminRepo,
		menuRepository:      menuRepository,
		loginInfoRepository: loginInfoRepo,
		validator:           validator.New(),
		captchaStore:        captchaStore,
	}
}

// Login 登录
func (s *LoginService) Login(c *gin.Context, dto dto.LoginDto) {
	// 获取IP地址和浏览器信息，用于记录登录日志
	ip := c.ClientIP()

	// 验证DTO
	if err := s.validator.Struct(dto); err != nil {
		s.createLoginInfo(dto.Username, ip, c, "参数验证失败", 2)
		response.Fail(c, response.ErrParams, err.Error())
		return
	}

	// 验证码是否过期
	code := s.captchaStore.Get(dto.CaptchaKey, false)
	if len(code) == 0 {
		s.createLoginInfo(dto.Username, ip, c, "验证码已过期", 2)
		response.Fail(c, response.Errcaptcha, response.GetMessage(response.Errcaptcha))
		return
	}

	// 校验验证码
	verifyRes := s.captchaStore.Verify(dto.CaptchaKey, dto.CaptchaImageValue, false)
	if !verifyRes {
		s.createLoginInfo(dto.Username, ip, c, "验证码不正确", 2)
		response.Fail(c, response.Errcaptcha, "验证码不正确")
		return
	}

	// 校验用户
	admin, err := s.adminRepository.SysAdminDetail(dto)
	if err != nil {
		s.createLoginInfo(dto.Username, ip, c, "查询用户失败", 2)
		response.Fail(c, response.ErrDatabase, "查询用户失败")
		return
	}

	// 校验密码 - 确保密码已经在数据库中以加密形式存储
	if !auth.VerifyPassword(dto.Password, admin.Password) {
		s.createLoginInfo(dto.Username, ip, c, "密码不正确", 2)
		response.Fail(c, response.ErrParams, "密码不正确")
		return
	}

	// 账号是否被禁用
	const status int = 2
	if admin.Status == status {
		s.createLoginInfo(dto.Username, ip, c, "账号已停用", 2)
		response.Fail(c, response.ErrParams, "账号已停用")
		return
	}

	// 生成token
	token, _, err := auth.GenerateToken(admin.ID, admin.Username)
	if err != nil {
		s.createLoginInfo(dto.Username, ip, c, "token生成失败", 2)
		response.Fail(c, response.ServerError, "token生成失败")
		return
	}

	// 记录成功登录日志
	s.createLoginInfo(dto.Username, ip, c, "登录成功", 1)

	// 获取菜单和权限
	leftMenuVo, err := s.getMenuList(admin.ID)
	if err != nil {
		response.Fail(c, response.ServerError, fmt.Sprintf("左侧菜单列表查询失败:%v", err.Error()))
		return
	}

	// 权限列表
	permissionList, err := s.menuRepository.QueryPermissionsByAdminId(admin.ID)
	if err != nil {
		response.Fail(c, response.ServerError, fmt.Sprintf("权限列表查询失败:%v", err.Error()))
		return
	}

	response.Success(c, map[string]interface{}{
		"token":          token,
		"sysAdmin":       admin,
		"leftMenuList":   leftMenuVo,
		"permissionList": permissionList,
	})
}

// 创建登录信息记录
func (s *LoginService) createLoginInfo(username, ip string, c *gin.Context, msg string, status int) {
	if s.loginInfoRepository == nil {
		return
	}

	loginInfo := model.SysLoginInfo{
		Username:      username,
		IpAddress:     ip,
		LoginLocation: util.GetRealAddressByIP(ip),
		Browser:       util.GetBrowser(c),
		Os:            util.GetOs(c),
		LoginStatus:   status,
		Message:       msg,
	}

	_ = s.loginInfoRepository.CreateLoginInfo(loginInfo)
}

// 获取菜单列表
func (s *LoginService) getMenuList(adminID int) ([]vo.LeftMenuVo, error) {
	var leftMenuVo []vo.LeftMenuVo

	leftMenuList, err := s.menuRepository.QueryMenuByAdmin(adminID)
	if err != nil {
		return nil, err
	}

	for _, menu := range leftMenuList {
		parentList, err := s.menuRepository.QueryMenuByAdminAndParent(adminID, menu.ID)
		if err != nil {
			continue
		}

		var menuSvoList []vo.MenuSvo
		for _, v := range parentList {
			menuSvoList = append(menuSvoList, vo.MenuSvo{
				MenuName: v.MenuName,
				Icon:     v.Icon,
				Url:      v.URL,
			})
		}

		leftMenuVo = append(leftMenuVo, vo.LeftMenuVo{
			Id:          menu.ID,
			MenuName:    menu.MenuName,
			Icon:        menu.Icon,
			Url:         menu.URL,
			MenuSvoList: menuSvoList,
		})
	}

	return leftMenuVo, nil
}
